﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ScrumTable.Common.Collections;
using ScrumTable.Common.Test;
using ScrumTable.DL.Data.Common;
using ScrumTable.DL.Data.Generic.Cache;
using ScrumTable.DL.Data.Generic.Test.Mocks;

using WorkItemBucket = ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>;
using WorkItemSavePair = ScrumTable.DL.Data.Generic.Cache.CacheSavePair<ScrumTable.DL.Data.Generic.Cache.CacheElementListEntryBucket<ScrumTable.DL.Data.Generic.Cache.CacheWorkItem>>;

#endregion

namespace ScrumTable.DL.Data.Generic.Test.Cache
{
    /// <summary>
    /// This is the test class for CacheWorkItem class.
    /// </summary>
    [TestClass]
    public class CacheWorkItemTest : CacheElementBaseTest<CacheWorkItem>
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        private CacheProject Project { get; set; }

        #endregion

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new CacheWorkItemTest instance.
        /// </summary>
        public CacheWorkItemTest()
        {
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Use TestInitialize to run code before running each test .
        /// </summary>
        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();

            Project = CacheContext.Projects.TryGetById(Resources.Project1.Id);
            ToTest = Project.WorkItems.TryGetById(ResourceManager.UserStory2Id);
        }

        /// <summary>
        /// Use TestCleanup to run code after each test has run.
        /// </summary>
        [TestCleanup]
        public override void TestCleanup()
        {
            ToTest = null;
            base.TestCleanup();
        }


        [TestMethod]
        public void TestChangeIterationPathFromServer()
        {
            TestChangeIterationPathFromServer(
                new ValueChain<string>(Project.UniqueId, Resources.Iteration1Proj1.UniqueId, Resources.UserStory3.UniqueId),
                Resources.Bug3.UniqueId,
                Resources.Iteration2Proj1);

            TestChangeIterationPathFromServer(
                new ValueChain<string>(Project.UniqueId, Resources.Iteration2Proj1.UniqueId, Resources.UserStory3.UniqueId),
                Resources.Bug3.UniqueId,
                Resources.Iteration1Proj1);
        }

        [TestMethod]
        public void TestChangeIterationPath()
        {
            // move current element
            ToTest.Iteration = Resources.Iteration1Proj1;

            // current elements must be moved
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration1Proj1.UniqueId), ToTest.UniqueId));

            // child elements must be moved
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration1Proj1.UniqueId, ToTest.UniqueId), Resources.Task1.UniqueId));
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration1Proj1.UniqueId, ToTest.UniqueId), Resources.Bug1.UniqueId));
        }

        [TestMethod]
        public void TestChangeIterationPathMultipleTimes()
        {
            TestChangeIterationPath();

            // move current element again
            ToTest.Iteration = Resources.Iteration2Proj1;

            // current elements must be moved
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration2Proj1.UniqueId), ToTest.UniqueId));

            // child elements must be moved
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration2Proj1.UniqueId, ToTest.UniqueId), Resources.Task1.UniqueId));
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.Iteration2Proj1.UniqueId, ToTest.UniqueId), Resources.Bug1.UniqueId));
        }


        [TestMethod]
        public void TestChangeIterationPathWithInconsistency()
        {
            // the Bug3 workitem contains another parent iteration path than its
            // parent workitem does. thus if you move the iteration path of Bug3 it should
            // not be moved in the workitem cache -> the path of the parent workitem
            // is stronger. 

            ValueChain<string> bug3ParentChain = new ValueChain<string>(Project.UniqueId, Resources.Iteration1Proj1.UniqueId, Resources.UserStory3.UniqueId);
            CacheWorkItem bug3 = Project.WorkItems.TryGetChild(bug3ParentChain, Resources.Bug3.UniqueId);
            CacheIteration newIteration = Resources.Iteration1Proj1;

            // make sure Bug3 is available
            Assert.IsNotNull(bug3);
            
            // change iteration path of bug3
            bug3.Iteration = newIteration;

            // bug3 must not be moved
            Assert.IsNotNull(Project.WorkItems.TryGetChild(bug3ParentChain, bug3.UniqueId));
            Assert.AreEqual(newIteration, bug3.Iteration);
        }

        [TestMethod]
        public void TestAttachments()
        {
            Assert.AreEqual(Resources.Attachment2, ToTest.Attachments.First());
            Assert.AreEqual(Resources.Attachment3, ToTest.Attachments.Second());
        }

        [TestMethod]
        public void TestSettings()
        {
            // get task to test
            CacheWorkItem task2 = Project.WorkItems.TryGetById(ResourceManager.Task2Id);

            // make sure test data are ready (ChangedSettings must always return all Settings)
            Assert.IsTrue(task2.ChangedSettings.ContainsKey(Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName));

            // first test: 2.0 should be written into the Settings
            task2[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName] = 2.0;

            // make sure data are avaialbe in the settings
            Assert.AreEqual(null, task2.ChangedSettings[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName].First());
            Assert.AreEqual(2.0, task2.ChangedSettings[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName].Second());

            // second test: overwrite settings
            task2[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName] = 3.0;
            Assert.AreEqual(null, task2.ChangedSettings[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName].First());
            Assert.AreEqual(3.0, task2.ChangedSettings[Project.OwnerContext.Schema.TaskSchema.CompletedWork.SchemaMappedName].Second());
        }

        [TestMethod]
        public void TestIsSynchronized()
        {
            // check initial state
            CacheWorkItem storyToTest = Project.WorkItems.TryGetById(ResourceManager.UserStory3Id);
            Assert.IsTrue(storyToTest.IsSynchronized);

            // change state
            Assert.IsTrue(AssertUtil.FiresChangeEvents(
                storyToTest,
                o => storyToTest.State = (string) storyToTest.Type.StateField.AllowedValues.Second(),
                "IsSynchronized"));

            // test if state change caused the IsSynchronized change
            Assert.IsFalse(storyToTest.IsSynchronized);

            // emulate data driver: accept the changes (a new workitem on server side is available)
            CacheDataChanges changes = DataContext.ExportChanges();
            CacheWorkItem storyToStore = changes.ChangedWorkItems.First().Value.Element;
            WorkItemBucket newBucket = new WorkItemBucket {Element = storyToStore};
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
                                                {
                                                    State = CacheSavePairState.Saved
                                                };

            changes.WorkItemsToSync[storyToStore.UniqueId] = newSavePair;

            // change state
            Assert.IsTrue(AssertUtil.FiresChangeEvents(
                storyToTest,
                o => DataContext.ImportChanges(changes),
                "IsSynchronized"));

            // test after accepting the changes the IsSynchronized flag must be set
            Assert.IsTrue(storyToTest.IsSynchronized);
        }


        [TestMethod]
        public void TestIsSynchronizedFailed()
        {
            // check initial state
            CacheWorkItem storyToTest = Project.WorkItems.TryGetById(ResourceManager.UserStory3Id);
            Assert.IsTrue(storyToTest.IsSynchronized);

            // change state
            Assert.IsTrue(AssertUtil.FiresChangeEvents(
                storyToTest,
                o => storyToTest.State = (string) storyToTest.Type.StateField.AllowedValues.Second(),
                "IsSynchronized"));

            // test if state change caused the IsSynchronized change
            Assert.IsFalse(storyToTest.IsSynchronized);

            // emulate data driver: accept the changes (a new workitem on server side is available)
            CacheDataChanges changes = DataContext.ExportChanges();
            CacheWorkItem storyToStore = changes.ChangedWorkItems.First().Value.Element;
            WorkItemBucket newBucket = new WorkItemBucket {Element = storyToStore};
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
                                                {
                                                    State = CacheSavePairState.Error
                                                };

            changes.WorkItemsToSync[storyToStore.UniqueId] = newSavePair;

            // change state
            Assert.IsFalse(AssertUtil.FiresChangeEvents(
                storyToTest,
                o => DataContext.ImportChanges(changes),
                "IsSynchronized"));

            // test after accepting the changes the IsSynchronized flag must be set
            Assert.IsFalse(storyToTest.IsSynchronized);
        }

        [TestMethod]
        public void TestState()
        {
            string newState = ToTest.Type.StateField.AllowedValues.Last().ToString();

            ToTest.State = newState;
            Assert.AreEqual(newState, ToTest.State);
            Assert.AreEqual(newState, ToTest.ChangedBuckets.First().Value);
            Assert.AreEqual(null, ToTest.ChangedBuckets.First().LastBackEndValue);

            foreach (string allowedState in ToTest.Type.StateField.AllowedValues)
            {
                TestStateChangeWithSettings(allowedState);
            }
        }

        [TestMethod]
        public void TestUnkownState()
        {
            const string newState = "SomeNewState";
            string stateBefore = ToTest.State;

            ToTest.State = newState;
            Assert.AreEqual(newState, ToTest.State);
            Assert.AreEqual(stateBefore, ToTest.ChangedBuckets.First().LastBackEndValue);

            foreach (string allowedState in ToTest.Type.StateField.AllowedValues)
            {
                TestStateChangeWithSettings(allowedState);
            }
        }

        [TestMethod]
        public void TestStateEventWithNewSettingsReset()
        {
            TestStateEventWithNewStateSettingsAndValue("Active", "InProgress");
            TestStateEventWithNewStateSettingsAndValue("Closed", null);
            TestStateEventWithNewStateSettingsAndValue("Active", null);
        }

        [TestMethod]
        public void TestStateEventWithNewSettingsAndValueWithInconsistency()
        {
            TestStateEventWithNewStateSettingsAndValue("InProgress", "Resolved");
        }

        [TestMethod]
        public void TestStateEventWithNewSettingsAndValue()
        {
            TestStateEventWithNewStateSettingsAndValue("InProgress", "InProgress");
        }

        [TestMethod]
        public void TestStateEventWithNewStateSettings()
        {
            TestStateEventWithNewStateSettingsAndValue(null, "Closed");
        }

        [TestMethod]
        public void TestStateEventWithNewStateValue()
        {
            TestStateEventWithNewStateSettingsAndValue("Closed", null);
        }

        [TestMethod]
        public void TestDeleteWorkItemUpdateFromServer()
        {
            // TODO: (geh) Implement this here...

            //int oldWiCount = Project.WorkItems.Count();

            //CacheWorkItem toDelete = Project.WorkItems.First();
            //CacheDataChanges changes = Project.GetChanges();
            //changes.WorkItemsToSync[toDelete.UniqueId] = new CacheSavePair<CacheElementListEntryBucket<CacheWorkItem>>(
            //    new CacheElementListEntryBucket<CacheWorkItem>
            //    {
            //        Element = null
            //    });
            //changes.WorkItemsToSync[toDelete.UniqueId].IsSaved = true;
            //changes.WorkItemsToSync[toDelete.UniqueId].SyncBack = false;
            //Project.MergeChanges(changes);

            //Assert.AreEqual(oldWiCount - 1, Project.WorkItems.Count());
            //Assert.IsFalse(Project.WorkItems.Contains(toDelete.UniqueId));
        }

        [TestMethod]
        public void TestNewStoryAndError()
        {
            const int serverSideNewWiId = 1003;

            // create & add work item
            ValueChain<string> parentIdChain = new ValueChain<string>(Project.UniqueId);
            CacheWorkItem newSeverWi = CacheContext.ElementFactory.CreateWorkItem(Project.UserStoryType);
            newSeverWi.Name = "[WorkItem 1003]";
            Project.WorkItems.Add(parentIdChain, newSeverWi);

            // rename another task
            CacheWorkItem taskToRename = Project.WorkItems.TryGetById(ResourceManager.Task2Id);
            taskToRename.Name = "Rename";

            // emulate data driver (first round): reject the changes and assign an id to the new [WorkItem 1003]
            CacheDataChanges changesFirstRound = DataContext.ExportChanges();
            Assert.AreEqual(CacheElementListEntryState.New, changesFirstRound.ChangedWorkItems[new ValueChain<string>(parentIdChain, newSeverWi.UniqueId)].State);

            // the element has been created on back-end system, assign id
            changesFirstRound.WorkItemsToSync[newSeverWi.UniqueId].Bucket.Element.AssignId(serverSideNewWiId);

            // create reject buckets
            WorkItemSavePair newSeverWiPair = new WorkItemSavePair(changesFirstRound.WorkItemsToSync[newSeverWi.UniqueId].Bucket)
                { State = CacheSavePairState.Error };
            
            WorkItemSavePair taskToRenamePair = new WorkItemSavePair(changesFirstRound.WorkItemsToSync[taskToRename.UniqueId].Bucket)
                { State = CacheSavePairState.Error };
            taskToRenamePair.Errors.Add(new CacheSaveError(new Exception()));
            
            changesFirstRound.WorkItemsToSync[newSeverWi.UniqueId] = newSeverWiPair;
            changesFirstRound.WorkItemsToSync[taskToRename.UniqueId] = taskToRenamePair;

            // import elements with errors
            DataContext.ImportChanges(changesFirstRound);

            // the newSeverWi element must have the ID 1003 assigned
            Assert.AreEqual(serverSideNewWiId, newSeverWi.Id);

            // emulate data driver (second round)
            CacheDataChanges changesSecondRound = DataContext.ExportChanges();
            Assert.AreEqual(CacheElementListEntryState.New, changesSecondRound.ChangedWorkItems[new ValueChain<string>(parentIdChain, newSeverWi.UniqueId)].State);
        }

        [TestMethod]
        public void TestNewTaskFromServerWithIterIncons()
        {
            int oldWiCount = Project.WorkItems.Count();

            // special case: in mean time there's a new workitem on server side
            CacheWorkItem newSeverWi = CacheContext.ElementFactory.CreateWorkItem(
                Project.TaskType, 200, "WorkItem 200",
                1, Resources.Iteration2Proj1.Id, Project.TaskType.Workflow.TransitionStart,
                string.Empty, Resources.Member1.DisplayName, 
                new Dictionary<string, object>(), new[] { CacheContext.ElementFactory.CreateWorkItemLink(Resources.UserStory2.Id) },
                new CacheWorkItemLink[0], new CacheAttachment[0], new CacheWorkItemRevision[0]);

            // emulate data driver: accept the changes (a new workitem on server side is available)
            CacheDataChanges changes = DataContext.ExportChanges();
            WorkItemBucket newBucket = new WorkItemBucket { Element = newSeverWi };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[newSeverWi.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // perpare event handling test
            DataAccessObjectEventTestStub eventTester = new DataAccessObjectEventTestStub(DataContext);
            eventTester.Action = DataAccessObjectChangedAction.Add;
            eventTester.ElementId = newSeverWi.UniqueId;
            eventTester.RegisterEvents(DataContext.Schema.TaskSchema);

            try
            {
                DataContext.ImportChanges(changes);
            }
            finally
            {
                // unregister all events
                eventTester.UnregisterEvents();
            }

            // test event handling
            Assert.AreEqual(1, eventTester.ExpectedEventFireCount);
            Assert.AreEqual(0, eventTester.OtherEventFireCount);

            Assert.AreEqual(oldWiCount + 1, Project.WorkItems.Count());
            Assert.IsNotNull(Project.WorkItems.TryGetById(newSeverWi.Id));
            Assert.IsNotNull(Project.WorkItems.TryGetById(newSeverWi.Id).AssignedToMemberChain);

            // task must be assigned to its parent user story chain. the inconsistency
            // of the parent 
            Assert.IsNotNull(Project.WorkItems.TryGetChild(new ValueChain<string>(Project.UniqueId, Resources.UserStory2.UniqueId), newSeverWi.UniqueId));
        }

        [TestMethod]
        public void TestNewStoryFromServer()
        {
            int oldWiCount = Project.WorkItems.Count();

            // special case: in mean time there's a new workitem on server side
            CacheWorkItem newSeverWi = CacheContext.ElementFactory.CreateWorkItem(
                Project.UserStoryType, 100, "WorkItem 100",
                1, CacheIdentity.DefaultId, Project.UserStoryType.Workflow.TransitionStart,
                string.Empty, Resources.Member1.DisplayName, 
                new Dictionary<string, object>(), new CacheWorkItemLink[0], new CacheWorkItemLink[0],
                new CacheAttachment[0], new CacheWorkItemRevision[0]);

            // emulate data driver: accept the changes (a new workitem on server side is available)
            CacheDataChanges changes = DataContext.ExportChanges();
            WorkItemBucket newBucket = new WorkItemBucket { Element = newSeverWi };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[newSeverWi.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // perpare event handling test
            DataAccessObjectEventTestStub eventTester = new DataAccessObjectEventTestStub(DataContext);
            eventTester.Action = DataAccessObjectChangedAction.Add;
            eventTester.ElementId = newSeverWi.UniqueId;
            eventTester.RegisterEvents(DataContext.Schema.UserStorySchema);

            try
            {
                DataContext.ImportChanges(changes);
            }
            finally
            {
                // unregister all events
                eventTester.UnregisterEvents();
            }

            // test event handling
            Assert.AreEqual(1, eventTester.ExpectedEventFireCount);
            Assert.AreEqual(0, eventTester.OtherEventFireCount);

            Assert.AreEqual(oldWiCount + 1, Project.WorkItems.Count());
            Assert.IsNotNull(Project.WorkItems.TryGetById(newSeverWi.Id));
            Assert.IsNotNull(Project.WorkItems.TryGetById(newSeverWi.Id).AssignedToMemberChain);
        }

        private void TestStateEventWithNewStateSettingsAndValue(string newState, string newStateSettings)
        {
            string currentState = ToTest.State;

            // touch name in order to get the work item in the change-set
            ToTest.Name = "NewName_" + Guid.NewGuid();

            CacheDataChanges changes = DataContext.ExportChanges();
            WorkItemBucket bucketToTest = changes.ChangedWorkItems.First().Value;

            // check whether the state of the clone is equal to the original state
            Assert.AreEqual(currentState, bucketToTest.Element.State);

            if (newStateSettings != null)
            {
                // set test values
                IDictionary<string, ICollection<object>> settings = new Dictionary<string, ICollection<object>>();
                settings[bucketToTest.Element.Type.StateField.BackEndId] = new[] { currentState, newStateSettings };
                bucketToTest.Element.ImportSettings(settings);
            }

            if (newState != null)
            {
                // may generate an inconsistency if also changed.
                // this property must be stronger than the settings.
                bucketToTest.Element.State = newState;
            }

            // import test values into data layer
            WorkItemSavePair newSavePair = new WorkItemSavePair(bucketToTest)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[bucketToTest.Element.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            // perpare event handling test.
            // CAUTION: ToTest as an object must be used and not the 'bucketToTest.Element' clone instance
            NotifyPropertyTestStub<CacheWorkItem, string> eventTester = new NotifyPropertyTestStub<CacheWorkItem, string>(
                ToTest,
                () => ToTest.State);

            eventTester.PropertyName = "State";
            eventTester.RegisterEvents();

            try
            {
                DataContext.ImportChanges(changes);
            }
            finally
            {
                // unregister all events
                eventTester.UnregisterEvents();
            }

            // test event handling
            Assert.AreEqual(1, eventTester.PropertyFiredCount);
            Assert.AreEqual(1, eventTester.PropertyFiringCount);
            Assert.AreEqual(newState ?? newStateSettings, eventTester.FiredValue);
            Assert.AreEqual(currentState, eventTester.FiringValue);
        }


        private void TestStateChangeWithSettings(string newAllowedState)
        {
            string lastOriginalValue = null;

            if (!ToTest.ChangedBuckets.IsEmpty())
            {
                lastOriginalValue = (ToTest.ChangedBuckets.First().OriginalValue as string);
            }

            ToTest.State = newAllowedState;
            Assert.AreEqual(newAllowedState, ToTest.State);

            if (lastOriginalValue != newAllowedState)
            {
                Assert.AreEqual(ToTest.Type.StateField.Schema.SchemaAllowedValueMapping[newAllowedState], ToTest.ChangedBuckets.First().LastBackEndValue);
            }
        }


        private void TestChangeIterationPathFromServer(ValueChain<string> parent, string expectedChild, CacheIteration newIterationId)
        {
            // make sure the story and child are available
            Assert.IsNotNull(Project.WorkItems.TryGetChild(parent.ParentChain, parent.Top));
            Assert.IsNotNull(Project.WorkItems.TryGetChild(parent, expectedChild));

            // prepare change
            CacheWorkItem parentStoryClone = (CacheWorkItem)Project.WorkItems.TryGetChild(parent.ParentChain, parent.Top).Clone();
            parentStoryClone.Iteration = newIterationId;

            CacheWorkItem childBugClone = (CacheWorkItem)Project.WorkItems.TryGetChild(parent, expectedChild).Clone();
            childBugClone.Iteration = newIterationId;

            CacheDataChanges changes = DataContext.ExportChanges();

            // create change import buckets for parent & child
            WorkItemBucket newChildBucket = new WorkItemBucket { Element = childBugClone };
            WorkItemSavePair newChildSavePair = new WorkItemSavePair(newChildBucket)
            {
                State = CacheSavePairState.Updated
            };

            WorkItemBucket newBucket = new WorkItemBucket { Element = parentStoryClone };
            WorkItemSavePair newSavePair = new WorkItemSavePair(newBucket)
            {
                State = CacheSavePairState.Updated
            };

            changes.WorkItemsToSync[childBugClone.UniqueId] = newChildSavePair;
            changes.WorkItemsToSync[parentStoryClone.UniqueId] = newSavePair;
            changes.RemoveDirtyFlags();

            DataContext.ImportChanges(changes);

            // child elements must be moved
            Assert.IsNull(Project.WorkItems.TryGetChild(parent.ParentChain, parent.Top));
            Assert.IsNull(Project.WorkItems.TryGetChild(parent, expectedChild));

            ValueChain<string> newParentChain = new ValueChain<string>(
                Project.UniqueId,
                newIterationId.UniqueId,
                parent.Top);

            Assert.IsNotNull(Project.WorkItems.TryGetChild(newParentChain.ParentChain, newParentChain.Top));
            Assert.IsNotNull(Project.WorkItems.TryGetChild(newParentChain, expectedChild));
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}
